# -*- coding: utf-8 -*-
# @Author  : LvShenkai
# @Time    : 2021/9/14 15:47
# @Function: DCP数据进行聚类
import datetime
import pandas
import numpy as np
from cluster.cluster_tool import Cluster_tool
from dataload.dataset import DataSet
from dataload.oracle_link import get_data_from_database, classify_train_and_test
from parameter.parameter import Parameter
import matplotlib.pyplot as plt


class Entrance:
    """
        非增量模型入口
    """
    @staticmethod
    def distance_cal(center: list, target: list):
        """
            计算两个向量的欧氏距离
        :param center: 聚类中心向量,只包含需要比较的字段，如比到TTL13，则0-11都不需要放入，只保存90-13
        :param target: 目标向量，只包含需要比较的字段
        :return: 两个向量之间的欧氏距离
        """
        distance = 0.0  # 距离
        assert len(center) == len(target)  # 两个向量长度必须相同
        for index in range(len(center)):
            distance += (center[index] - target[index]) ** 2
        return distance

    @staticmethod
    def predict(centers: list, index: int, target: list):
        """
            找出目标数据最近的聚类中心
        :param centers: 所有的聚类中心，字典形式给出
        :param index: 聚类索引
        :param target: 目标数据，只包含需要聚类的数据列，其余的不要
        :return: 返回对应的聚类标签，即是哪一类
        """
        df_centers_complete = DataSet.data_load(centers, phase='center')  # 转换为df
        df_centers_complete = df_centers_complete[df_centers_complete['index'] == index]  # 筛选出对应索引的聚类中心
        # 只保留需要的列
        need_columns = ['cluster_label']
        need_columns.extend(Parameter.COLUMNS_DICT_CENTER.value[index])
        df_centers = df_centers_complete[need_columns]

        min_center = 0  # 最接近的聚类中心
        min_distance = float('inf')  # 最小距离
        # 按行遍历df_centers，找出距离最小的聚类中心
        for index, row in df_centers.iterrows():
            center_list = list(row)  # 当前聚类中心
            dictance = Entrance.distance_cal(center_list[1:], target)  # 计算距离
            if dictance <= min_distance:
                min_center = center_list[0]
                min_distance = dictance

        # 对应的聚类中心类别的ITTLN1均值为预测值
        a = df_centers_complete[df_centers_complete['cluster_label'] == min_center]['DN1_mean']
        predict_ITTLN1 = a.iloc[0]
        return int(min_center), predict_ITTLN1

    @staticmethod
    def test(centers: list, datas_test: list):
        """
            对测试机数据进行测试
        :param centers: 聚类中心
        :param datas_test: 测试集数据
        :return: 返回对测试集的预测结果
        """
        df_datas_test = DataSet.data_load(datas_test, phase='test')  # 转为DataFrame格式
        df_datas_test.reset_index(drop=True, inplace=True)

        # 用于保存预测结果
        result_test_predict = pandas.DataFrame(columns=Parameter.COLUMNS_PREDICT_RESULT.value)
        FLIGHTDATE = df_datas_test['FLIGHTDATE'].tolist()
        ITTLN1 = df_datas_test['ITTLN1'].tolist()
        result_test_predict['FLIGHTDATE'] = FLIGHTDATE
        result_test_predict['ITTLN1'] = ITTLN1
        df_datas_test.reset_index(drop=True, inplace=True)
        # 遍历所有数据
        for i, row in df_datas_test.iterrows():
            print('正在预测第', i, '个数据')
            # 遍历所有index
            for index in Parameter.INDEXS.value:
                target = list(row[Parameter.COLUMNS_DICT_CENTER.value[index]])
                cluster_label, predict_ITTLN1 = Entrance.predict(centers, index, target)
                column = 'C' + str(index)
                result_test_predict.at[i, column] = round(predict_ITTLN1)

        return result_test_predict

    @staticmethod
    def cluster_entrance(data_train: list) -> list:
        """
            聚类入口，传入训练数据，返回聚类中心
        :param data_train: 训练数据，格式为由dict组成的list，例:[{'ITTL360': 1, 'ITTL180': 3},{'ITTL360': 2, 'ITTL180': 5}]
        :return: 返回聚类中心，格式为由dict组成的list，例:
                [{'index':180,'cluster_label':0,'ITTL360': 1, 'ITTL180': 3},
                {'index':180,'cluster_label':1,'ITTL360': 2, 'ITTL180': 5}]
        """

        # 加载数据
        data = DataSet.data_load(data_train)

        # 构造聚类工具实例
        cluster_tool = Cluster_tool(data)

        # 所有的聚类中心
        cluster_centers = pandas.DataFrame(columns=Parameter.COLUMNS_CENTER.value)

        # 依次聚类,通过肘部拐点计算确定聚类中心个数
        # for end in Parameter.END.value:
        #     # 获得最佳的聚类中心个数
        #     best_num_center, elbow_result = cluster_tool.elbow_rule(max_num_center=25, start=Parameter.START.value,
        #                                                             end=end)
        #
        #     # 画出拐点图
        #     elbow_result = np.array(elbow_result)
        #     num_center = np.array([i for i in range(1, len(elbow_result) + 1)])
        #     plt.plot(num_center, elbow_result, lw=2)
        #     # f = plt.gcf()
        #     # f.savefig(r'../拐点图/{}.png'.format(end))
        #     # plt.show()
        #
        #     # 进行聚类
        #     df_data, df_cluster_center = cluster_tool.k_means(start=Parameter.START.value, end=end,
        #                                                       num_center=best_num_center)
        #     # 记录聚类中心
        #     cluster_centers = pandas.concat([cluster_centers, df_cluster_center])

        # 指定聚类中心个数
        for end in Parameter.END.value:
            best_num_center = Parameter.FIXED_NUM_CENTER.value
            # 进行聚类
            df_data, df_cluster_center = cluster_tool.k_means(start=Parameter.START.value, end=end,
                                                              num_center=best_num_center)
            # 记录聚类中心
            cluster_centers = pandas.concat([cluster_centers, df_cluster_center])
        centers_dic_list = DataSet.df_2_dic_list(cluster_centers)
        return centers_dic_list


if __name__ == '__main__':
    data_list_dic = get_data_from_database()
    data_train, data_test = classify_train_and_test(data_list_dic)
    centers, cluster_centers_df = Entrance.cluster_entrance(data_train)
    print('#' * 100)
    print('\033[33mcenter\033[0m')
    for i in centers:
        print(i)

    # 预测
    df_data_test = Entrance.test(centers, data_test)
    print(df_data_test)

    rmses = [0, 0]
    a = df_data_test['ITTLN1']
    b = a.tolist()
    num_test = df_data_test.shape[0]  # 测试集行数
    TTLN1 = np.array(df_data_test['ITTLN1'].tolist())
    column_test = list(df_data_test)
    for i in range(2, len(column_test)):
        cur_TTL = np.array(df_data_test[column_test[i]].tolist())
        cur_rmse = np.sqrt(((cur_TTL - TTLN1) ** 2).sum() / num_test)
        rmses.append(cur_rmse)
    print(rmses)
